package metaphor.persistence.filter;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Mark Ashworth
 * @version 1.0.0
 */
public class CompoundFilter implements Expression {

	private List<Object> expression = new ArrayList<Object>();

	public CompoundFilter() {
		super();
	}

	public CompoundFilter(Expression operand) {
		super();
		this.expression.add(operand);
	}
	
	public CompoundFilter(String fieldName, Operator operator, Serializable operand) {
		super();
		this.expression.add(compose(fieldName, operator, operand));
	}
	
	/**
	 * The following expresses that the price should be greater or equal to
	 * 100.<br />
	 * <i>SimpleFilter.ge("price", 100);</i>
	 * 
	 * @param fieldName The name of the field
	 * @param operand The operand value
	 * @return CompoundFilter
	 */
	public static CompoundFilter ge(String fieldName, Serializable operand) {
		return new CompoundFilter(new SimpleExpression(fieldName, Operator.GE, operand));
	}
	
	/**
	 * The following expresses that the price should be greater than
	 * 100.<br />
	 * <i>SimpleFilter.gt("price", 100);</i>
	 * 
	 * @param fieldName The name of the field
	 * @param operand The operand value
	 * @return CompoundFilter
	 */
	public static CompoundFilter gt(String fieldName, Serializable operand) {
		return new CompoundFilter(new SimpleExpression(fieldName, Operator.GT, operand));
	}
	
	/**
	 * The following expresses that the price should be less or equal to
	 * 100.<br />
	 * <i>SimpleFilter.le("price", 100);</i>
	 * 
	 * @param fieldName The name of the field
	 * @param operand The operand value
	 * @return CompoundFilter
	 */
	public static CompoundFilter le(String fieldName, Serializable operand) {
		return new CompoundFilter(new SimpleExpression(fieldName, Operator.LE, operand));
	}
	
	/**
	 * The following expresses that the price should be less than
	 * 100.<br />
	 * <i>SimpleFilter.lt("price", 100);</i>
	 * 
	 * @param fieldName The name of the field
	 * @param operand The operand value
	 * @return CompoundFilter
	 */
	public static CompoundFilter lt(String fieldName, Serializable operand) {
		return new CompoundFilter(new SimpleExpression(fieldName, Operator.LT, operand));
	}
	
	/**
	 * The following expresses that the price should be equal to
	 * 100.<br />
	 * <i>SimpleFilter.eq("price", 100);</i>
	 * 
	 * @param fieldName The name of the field
	 * @param operand The operand value
	 * @return CompoundFilter
	 */
	public static CompoundFilter eq(String fieldName, Serializable operand) {
		return new CompoundFilter(new SimpleExpression(fieldName, Operator.EQ, operand));
	}
	
	/**
	 * The following expresses that the name should be like '%JO%'.<br />
	 * <i>SimpleFilter.like("name", "%JO%");</i>
	 * 
	 * @param fieldName The name of the field
	 * @param operand The operand value
	 * @return CompoundFilter
	 */
	public static CompoundFilter like(String fieldName, Serializable operand) {
		return new CompoundFilter(new SimpleExpression(fieldName, Operator.LIKE, operand));
	}
	
	/**
	 * Used to create a filter that is composed of a field name, operator and
	 * an operand. The default implementation uses the SimpleFilter class
	 * but implementations can make use of another Filter class instead.
	 * 
	 * @param fieldName The field name
	 * @param operator The operator
	 * @param operand The operand
	 * @return Filter
	 */
	protected Expression compose(String fieldName, Operator operator, Serializable operand) {
		return new SimpleExpression(fieldName, operator, operand);
	}
	
	/**
	 * The following constructs a logical and between the previous operand
	 * and this operand.
	 * @param operand The filter operand
	 * @return CompoundFilter
	 */
	public CompoundFilter and(Expression operand) {
		if (operand != null) {
			this.expression.add(Logical.AND);
			this.expression.add(operand);
		}
		return this;
	}
	
	/**
	 * The following constructs a logical AND between the previous operand
	 * on this operand.<br />
	 * <i>new CompoundFilter(SimpleFilter.gt("price", 100)).and(SimpleFilter.lt("price", 200));</i><br />
	 * <i>new CompoundFilter("price", Operator.GT, 100).and("price", Operator.LT, 200);</i>
	 * @param fieldName The name of the field
	 * @param operator The operator
	 * @param operand The operand value
	 * @return CompoundFilter
	 */
	public CompoundFilter and(String fieldName, Operator operator, Serializable operand) {
		
		if (fieldName != null && operator != null) {
			this.expression.add(Logical.AND);
			this.expression.add(compose(fieldName, operator, operand));
		}
		return this;
	}
	
	/**
	 * The following constructs a logical OR between the previous operand
	 * and this operand.
	 * @param operand The filter operand
	 * @return CompoundFilter
	 */
	public CompoundFilter or(Expression operand) {
		if (operand != null) {
			this.expression.add(Logical.OR);
			this.expression.add(operand);
		}
		return this;
	}
	
	/**
	 * The following constructs a logical OR between the previous operand
	 * on this operand.<br />
	 * <i>new CompoundFilter(SimpleFilter.gt("price", 100)).or(SimpleFilter.lt("price", 200));</i><br />
	 * <i>new CompoundFilter("price", Operator.LT, 100).or("price", Operator.GT, 200);</i>
	 * @param fieldName The name of the field
	 * @param operator The operator
	 * @param operand The operand value
	 * @return CompoundFilter
	 */
	public CompoundFilter or(String fieldName, Operator operator, Serializable operand) {
		
		if (fieldName != null && operator != null) {
			this.expression.add(Logical.OR);
			this.expression.add(compose(fieldName, operator, operand));
		}
		return this;
	}

//	public String getExpression(FilterLanguage filterLanguage) {
//		StringBuffer filter = new StringBuffer();
//		
//		for (Object object : expression) {
//			if (object instanceof Logical) {
//				filter.append(" ").append(filterLanguage.logical((Logical) object)).append(" ");
//			} else if (object instanceof CompoundFilter) {
//				filter
//						.append("(")
//						.append(((CompoundFilter) object).getExpression(filterLanguage))
//						.append(")");
//			} else if (object instanceof Expression) {
//				filter.append(((Expression) object).getExpression(filterLanguage));
//			}
//		}
//		return filter.toString();
//	}
}
